﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace JeuDeDames_v1._02
{
    [Serializable]
    public abstract class Joueur
    {
        #region Constantes
        public const int BLANC = -1;
        public const int NOIR = 1;
        #endregion

        #region Propriétés
        public string nom;
        public int couleur;     //BLANC ou NOIR
        public bool monTour;    //indique si c'est le tour du joueur à jouer.
        public List<Piece> lesPieces;
        public List<Coup> coupsPossibles;
        public int[] indicePromote; //position des 5 cases du damier.
        public int nbDames=0;   //sert dans Damier.finPartie(), représente le nombre de dames du joueur.
        public int nbPieces=0;    //nombre de pieces du joueur.

        public List<int> piecesMangees; //représente toute les cases qui sont mangés lors d'une rafle.
        #endregion

        #region Méthodes

        #region Calculs
        public bool coupsMangeables()
        {
            if (coupsPossibles.Count > 0)
                return coupsPossibles[0].mange();
            return false;
        }

        public void calculCoupsPossibles(Damier d)
        {
            List<Coup> coupsPossibleSansPrise = new List<Coup>();
            List<Coup> coupsPossibleAvecPrise = new List<Coup>();
            int N = 4;
            int[] direction = new int[4] { 9, 11, -9, -11 };
            foreach (Piece p in lesPieces)
            {
                try
                {
                    for (int n = 0; n < N; n++) //Pour chaque direction
                    { 
                        try
                        {
                            int i = 1;
                            bool piece = false;  //true signifie si il y a une piece, false sinon
                            int sens = this.couleur * direction[n];
                            /*tant que dans la direction n, les cases sont vides*/
                            try     //On parcourt toutes les cases vides.
                            {
                                while (i <= p.pas && (d.damier[p.pos + i * sens] as CaseN).vide())
                                {
                                    if (!(p is Pion && n > 1))//!(si c'est un pion ET que l'on va vers l'arrière);
                                    {
                                        coupsPossibleSansPrise.Add(new Coup(d.nbTour, d.damier[p.pos] as CaseN, d.damier[p.pos + i * sens] as CaseN));
                                    }    
                                    i++;
                                }
                                if (i > p.pas)    //Si on a atteind le pas.
                                    piece = false;
                                else
                                    piece = true;   //Si on sort à cause d'une piece sur la ieme case.
                            }
                            catch (IndexOutOfRangeException) { i--; piece = false; }   //on est sorti des limites du tab, donc on revient un crant en arriere;

                            /*Si la case contient une piece
                                * ET qui est adverse 
                                * ET qu'après il y a une case vide
                                * ==> CAS OU ON PEUT MANGE <==*/
                            if (piece == true 
                                && (d.damier[p.pos + i * sens] as CaseN).piece.j.couleur != this.couleur
                                && (d.damier[p.pos + (i + 1) * sens] as CaseN).vide())
                            {
                                int pos_pieceMangee = i;
                                i++;
                                try     //TQ les cases sont vides 
                                {
                                    //TQ il reste des cases où mettre la piece.
                                    while (((i - pos_pieceMangee) <= p.pas) && (d.damier[p.pos + i * sens] as CaseN).vide())
                                    {
                                        coupsPossibleAvecPrise.Add(new Coup(d.nbTour, d.damier[p.pos] as CaseN, d.damier[p.pos + pos_pieceMangee * sens] as CaseN, d.damier[p.pos + i * sens] as CaseN));
                                        i++;
                                    }
                                }
                                catch (IndexOutOfRangeException) { }
                            }
                        }
                        catch (IndexOutOfRangeException) { continue; }  //continuer la boucle for avec n++;
                        catch (NullReferenceException) { continue; }
                    }
                }
                catch (NullReferenceException) { }
                catch (IndexOutOfRangeException) { }
            }
            if (coupsPossibleAvecPrise.Count() > 0)
            {
                this.coupsPossibles = coupsPossibleAvecPrise;
                List<Coup> coupsAJouer = new List<Coup>();
                this.calculMangeageMultiple(d, this.coupsPossibles, ref coupsAJouer);
                this.coupsPossibles = coupsAJouer;
            }
            else
                this.coupsPossibles = coupsPossibleSansPrise;
            
        }// calculCoupsPossibles();

        /*calculMangeageMultiple(Damier, Liste des coups non triés, liste des coups triés)*/
        protected int calculMangeageMultiple(Damier d, List<Coup> coupsPossibles, ref List<Coup> coupsAJouer, int profondeur = 0)
        {
            int mangerMax = int.MinValue;
            int nombreManger = 0;
            if (coupsPossibles.Count() == 0)
            {
                nombreManger = profondeur;
                return nombreManger;
            }
            foreach (Coup unCoup in coupsPossibles)
            {
                unCoup.orig.piece.mange(unCoup);
                List<Coup> apres = this.calculCoupsPossibleQuiMangentApres(d, unCoup);  //calcul les coups possibles après avoir joué unCoup.
                nombreManger = calculMangeageMultiple(d, apres, ref coupsAJouer, profondeur + 1);
                if (nombreManger > mangerMax)
                {
                    mangerMax = nombreManger;
                    if (profondeur == 0)
                    {
                        coupsAJouer.Clear();
                        coupsAJouer.Add(unCoup);
                    }
                }
                else if (nombreManger == mangerMax)
                {
                    if (profondeur == 0)
                        coupsAJouer.Add(unCoup);
                }
                d.annulerCoup(unCoup);
            }
            return mangerMax;
        }

        protected List<Coup> calculCoupsPossibleQuiMangentApres(Damier d, Coup unCoup)
        {
            List<Coup> coupsPossiblesAvecPrise = new List<Coup>();
            //int[] direction = null;
            int[] direction = new int[4] { 9, 11, -9, -11 };
            try
            {
                Piece p = unCoup.dest.piece;    //Le coup "unCoup" a déja été joué, donc la piece se situe sur dest.
                for (int n = 0; n < 4; n++) //Pour chaque direction (sauf celle de départ). 
                {
                    try
                    {
                        int i = 1;
                        bool piece = false;  //true signifie si il y a une piece, false sinon
                        int sens = this.couleur * direction[n];
                        /*tant que dans la direction n, les cases sont vides*/
                        try     //On parcourt toutes les cases vides.
                        {
                            while (i <= p.pas && (d.damier[p.pos + i * sens] as CaseN).vide()/* && !this.piecesMangees.Contains(p.pos + (i * sens))*/)
                            {
                                i++;
                            }
                            if (i > p.pas)
                            {    //Si on a atteind le pas.
                                piece = false;
                                continue;   //on passe au n suivant car on ne peut pas manger;
                            }
                            else
                                piece = true;   //Si on sort à cause d'une piece sur la ieme case.
                        }
                        catch (IndexOutOfRangeException) { i--; piece = false; continue; }   //on est sorti des limites du tab, donc on revient un crant en arriere;

                        /*Si la case contient une piece
									* ET qui est adverse 
									* ET qu'après il y a une case vide
									* ==> CAS OU ON PEUT MANGE <==*/
                        if (piece == true
                            && (d.damier[p.pos + i * sens] as CaseN).piece.j.couleur != this.couleur
                            && (d.damier[p.pos + (i + 1) * sens] as CaseN).vide())
                        {
                            int pos_pieceMangee = i;
                            i++;
                            try     //TQ les cases sont vides 
                            {
                                //TQ il reste des cases où mettre la piece.
                                while (((i - pos_pieceMangee) <= p.pas) && (d.damier[p.pos + i * sens] as CaseN).vide()/* && !this.piecesMangees.Contains(p.pos + (i  * sens))*/)
                                {
                                    coupsPossiblesAvecPrise.Add(new Coup(d.nbTour, d.damier[p.pos] as CaseN, d.damier[p.pos + pos_pieceMangee * sens] as CaseN, d.damier[p.pos + i * sens] as CaseN));
                                    i++;
                                }
                            }
                            catch (IndexOutOfRangeException) { }
                        }
                    }
                    catch (IndexOutOfRangeException) { continue; }  //continuer la boucle for avec n++;
                    catch (NullReferenceException) { continue; }
                }
            }
            catch (NullReferenceException) { }
            catch (IndexOutOfRangeException) { }

            return coupsPossiblesAvecPrise;
        }
        #endregion

        #region Affichage
        public void afficheCoupsPossibles()
        {
            foreach (Coup unCoup in this.coupsPossibles)
            {
                Console.Write(unCoup + " ");
            }
            Console.WriteLine();
        }//afficheCoupsPossibles();

        public void affichePiecesMobiles()
        {
            List<int> indicesPiecesMobiles = new List<int>();
            foreach (Coup unCoup in this.coupsPossibles)
            {
                if (!(indicesPiecesMobiles.Contains(unCoup.orig.pos)))
                    indicesPiecesMobiles.Add(unCoup.orig.pos);
            }
            foreach (int p in indicesPiecesMobiles)
                Console.WriteLine(Coup.convertPosition(p) + " ");
        }

        public void affichePieceMange()
        {
            foreach (int inteuh in this.piecesMangees)
            {
                Console.Write(Coup.convertPosition(inteuh) + " ");
            }
            Console.WriteLine();
        }
        #endregion

        #region Autres
        public void viderPiecesMangees(Damier d)
        {
            foreach (int caseN in this.piecesMangees)
            {
                (d.damier[caseN] as CaseN).piece = null;
            }
            this.piecesMangees.Clear();
        }

        public void jouer(Damier d, ref Coup coup, bool premierCoup = true)   //j représente le joueur qui doit jouer. j permettra d'appeler minMax dans le cas des test de l'humain.
        {
            try
            {
                if (!d.historiqueCoups.Contains(coup))
                    d.historiqueCoups.Add(coup);
                if (coup.mange())   //si le coup mange
                {
                    coup.orig.piece.mange(coup);

                    List<Coup> temp = this.calculCoupsPossibleQuiMangentApres(d, coup);
                    if (temp.Count > 0)
                    {
                        List<Coup> coupsAJouer = new List<Coup>();
                        this.calculMangeageMultiple(d, temp, ref coupsAJouer);
                        coupsPossibles = coupsAJouer;

                        if (this is Humain)
                            d.afficheDamier();
                        if (this.coupsMangeables())
                        {
                            if (this is Humain)
                            {
                                this.afficheCoupsPossibles();
                                coup = (this as Humain).coupAJouer(d);   //utilisateur rentre ses coordonnées. Celles-ci doivent correspondre à un coup présent dans la liste coupPossible.
                            }
                            else if (this is Ordinateur)
                            {
                                if (this.coupsPossibles.Count() > 1)
                                    //coup = (this as Ordinateur).minMaxPremier(d);
                                    coup = (this as Ordinateur).coupAleatoire();
                                else if (this.coupsPossibles.Count() == 1)
                                    coup = this.coupsPossibles[0];
                            }
                            jouer(d, ref coup, false);
                        }
                    }
                }
                else if (premierCoup)    //si il ne mange pas alors c'est un déplacement. Si c'est un déplacement c'est obligatoirement le premier coup.
                {
                    coup.orig.piece.seDeplacer(coup);
                }
            }
            catch (NullReferenceException) { }
        }

        public int evaluationMangeageMultiple(Damier d, Joueur j, Coup dernierCoupJoue)
        {
            int valeur = 1;

            Joueur adv = null;
            if (j == d.blanc)
            {
                adv = d.noir;
            }
            else if (j == d.noir)
            {
                adv = d.blanc;
            }
            adv.calculCoupsPossibles(d);
            foreach (Coup unCoup in coupsPossibles)
            {
                if (unCoup.pMangee != null && unCoup.pMangee == dernierCoupJoue.dest) // TODO REVOIR
                    if (unCoup.pMangee.piece is Dame)
                        return -3;
                    else
                        return -1;
            }
            return valeur;
        }
        #endregion
        #endregion
    }
}
